//package java_cup.runtime;

//import java.util.Arrays;
//import java.util.IEnumerator;
//import java.util.LinkedList;
//import java.util.List;

//import javax.xml.stream.XMLStreamException;
//import javax.xml.stream.System.Xml.XmlWriter;

//import java_cup.runtime.ComplexSymbolFactory.ComplexSymbol;
//import java_cup.runtime.ComplexSymbolFactory.Location;

using System.ComponentModel.DataAnnotations;
using static ComplexSymbolFactory;

public abstract class XMLElement {
    public abstract List<XMLElement> selectById(String s);

    public void dump(System.Xml.XmlWriter writer, XMLElement elem, params String[] blacklist) /*throws XMLStreamException*/ {
        dump(null, writer, elem, blacklist);
    }

    public void dump(ScannerBuffer buffer, System.Xml.XmlWriter writer, XMLElement elem, params String[] blacklist) {
        //writer.WriteStartDocument("utf-8", "1.0");
        writer.WriteProcessingInstruction("xml-stylesheet", "href=\"tree.xsl\" type=\"text/xsl\"");
        writer.WriteStartElement("document");

        if (blacklist.Length > 0) {
            writer.WriteStartElement("blacklist");
            foreach (String s in blacklist) {
                writer.WriteStartElement("SymbolDraft");
                writer.WriteString(s);
                writer.WriteEndElement();
            }
            writer.WriteEndElement();
        }

        writer.WriteStartElement("parsetree");
        elem.dump(writer);
        writer.WriteEndElement();

        if (buffer != null) {
            writer.WriteStartElement("tokensequence");
            foreach (Node s in buffer.Buffered) {
                if (s is ComplexSymbol) {
                    ComplexSymbol cs = (ComplexSymbol)s;
                    if (cs.value != null) {
                        writer.WriteStartElement("token");
                        writer.WriteAttributeString("name", cs.Name);
                        cs.Left.toXML(writer, "_left");
                        writer.WriteString(cs._value + "");
                        cs.Right.toXML(writer, "_right");
                        writer.WriteEndElement();
                    }
                    else {
                        writer.WriteStartElement("keyword");
                        writer.WriteAttributeString("_left", cs.Left + "");
                        writer.WriteAttributeString("_right", cs.Right + "");
                        writer.WriteString(cs.Name + "");
                        writer.WriteEndElement();
                    }
                }
                else if (s is Node) {
                    writer.WriteStartElement("token");
                    writer.WriteString(s.ToString());
                    writer.WriteEndElement();
                }
            }
            writer.WriteEndElement();
        }
        writer.WriteEndElement();
        writer.WriteEndDocument();
        writer.Flush();
        writer.Close();
    }

    public String tagname;

    public String Tagname => tagname;

    public abstract Location right();

    public abstract Location left();

    public abstract void dump(System.Xml.XmlWriter writer);

    public virtual List<XMLElement> getChildren() {
        return new();
    }

    public virtual bool hasChildren() {
        return false;
    }

    public class NonTerminalXml : XMLElement {
        /*@Override*/
        public override bool hasChildren() {
            return list.Count > 0;
        }

        /*@Override*/
        public override List<XMLElement> getChildren() {
            return list;
        }

        /*@Override*/
        public override List<XMLElement> selectById(String s) {
            List<XMLElement> response = new();
            if (tagname.Equals(s))
                response.Add(this);
            foreach (XMLElement e in list) {
                List<XMLElement> selection = e.selectById(s);
                response.AddRange(selection);
            }
            return response;
        }

        public int variant;

        public int getVariant() {
            return variant;
        }

        List<XMLElement> list;

        public NonTerminalXml(String tagname, int variant, params XMLElement[] l) {
            this.tagname = tagname;
            this.variant = variant;
            list = new(l);
        }

        /*@Override*/
        public override Location left() {
            foreach (XMLElement e in list) {
                Location loc = e.left();
                if (loc != null)
                    return loc;
            }
            return null;
        }

        /*@Override*/
        public override Location right() {
            var query = from item in list orderby item descending select item;
            foreach (var it in query) {
                Location loc = it.right();
                if (loc != null)
                    return loc;
            }
            return null;
        }

        /*@Override*/
        public override string ToString() {
            if (list.Count == 0) {
                return "<nonterminal id=\"" + tagname + "\" variant=\"" + variant + "\" />";
            }
            String ret = "<nonterminal id=\"" + tagname + "\" _left=\"" + left() + "\" _right=\"" + right()
                    + "\" variant=\"" + variant + "\">";
            foreach (XMLElement e in list)
                ret += e.ToString();
            return ret + "</nonterminal>";
        }

        /*@Override*/
        public override void dump(System.Xml.XmlWriter writer) /*throws XMLStreamException*/ {
            writer.WriteStartElement("nonterminal");
            writer.WriteAttributeString("id", tagname);
            writer.WriteAttributeString("variant", variant + "");
            // if (!list.isEmpty()){
            Location loc = left();
            if (loc != null)
                loc.toXML(writer, "_left");
            // }
            foreach (XMLElement e in list)
                e.dump(writer);
            loc = right();
            if (loc != null)
                loc.toXML(writer, "_right");
            writer.WriteEndElement();
        }
    }

    public class ExceptionXml : XMLElement {
        /*@Override*/
        public override bool hasChildren() {
            return false;
        }

        /*@Override*/
        public override List<XMLElement> selectById(String s) {
            return new();
        }

        Location l, r;

        public ExceptionXml(Location l, Location r) {
            this.l = l;
            this.r = r;
        }

        /*@Override*/
        public override Location left() {
            return l;
        }

        /*@Override*/
        public override Location right() {
            return r;
        }

        /*@Override*/
        public override string ToString() {
            return "<error _left=\"" + l + "\" _right=\"" + r + "\"/>";
        }

        /*@Override*/
        public override void dump(System.Xml.XmlWriter writer) /*throws XMLStreamException*/ {
            writer.WriteStartElement("error");
            writer.WriteAttributeString("_left", left() + "");
            writer.WriteAttributeString("_right", right() + "");
            writer.WriteEndElement();
        }
    }

    public class TerminalXml : XMLElement {
        /*@Override*/
        public override bool hasChildren() {
            return false;
        }

        /*@Override*/
        public override List<XMLElement> selectById(String s) {
            List<XMLElement> ret = new();
            if (tagname.Equals(s)) {
                ret.Add(this);
            }
            return ret;
        }

        Location l;
        Location r;
        Object _value;

        public TerminalXml(Location l, String symbolname, Location r) :
            this(l, symbolname, null, r) {
        }

        public TerminalXml(Location l, String symbolname, Object i, Location r) {
            this.l = l;
            this.r = r;
            _value = i;
            tagname = symbolname;
        }

        public Object value() {
            return _value;
        }

        /*@Override*/
        public override Location left() {
            return l;
        }

        /*@Override*/
        public override Location right() {
            return r;
        }

        /*@Override*/
        public override string ToString() {
            return (_value == null) ? "<terminal id=\"" + tagname + "\"/>"
                    : "<terminal id=\"" + tagname + "\" _left=\"" + l + "\" _right=\"" + r + "\">" + _value
                            + "</terminal>";
        }

        /*@Override*/
        public override void dump(System.Xml.XmlWriter writer) /*throws XMLStreamException*/ {
            writer.WriteStartElement("terminal");
            writer.WriteAttributeString("id", tagname);
            writer.WriteAttributeString("_left", left() + "");
            writer.WriteAttributeString("_right", right() + "");
            if (value != null)
                writer.WriteString(_value + "");
            writer.WriteEndElement();
        }
    }
}
